﻿#include "StdAfx.h"
#include "Controller.h"
#include "AlgorithmMain.h"
#include "TestGUI.h"
#include "ResultGUI.h"
#include "LCMFirstStep.h"


using namespace System;
using namespace System::IO;
using namespace System::Drawing;



// PUBLIC METHODEN

// Beschreibung der Methoden in Header-Datei


Controller::Controller() {
	polygon = gcnew Polygon();
}

void Controller::setGUI(TestGUI^ gui) {
	this->gui = gui;
}

void Controller::setModel(AlgorithmMain^ m) {
	this->model = m;
}

void Controller::setImagePath(System::String^ path) {

	// Übernehme Ausgangsbild und zeige es an

	this->imagePath = path;
	Image^ image = Image::FromFile(path);
	this->width = 500;
	this->height = 300;

	this->queryImage = gcnew Bitmap(image, this->width, this->height);;

	this->mask = gcnew array<bool,2>(this->width, this->height);
	this->borderMask = gcnew array<bool,2>(this->width, this->height);
	this->gui->setImage(queryImage);

}

void Controller::setDatabasePath(System::String^ path) {
	
	// Übernehme Pfad aus Datenbank und zeige die Kategorien an

	this->databasePath = path;
	DirectoryInfo^ di = gcnew DirectoryInfo(path);
	array<DirectoryInfo^>^ dirinfos =  di->GetDirectories();
	array<String^>^ dirs = gcnew array<String^>(dirinfos->GetLength(0));
	int i = 0;
	for each (DirectoryInfo^ dirinfo in dirinfos) {
		dirs[i] = dirinfo->Name;
		i++;
	}
	gui->setCategories(dirs);
}

void Controller::setFinalCategory(System::String^ path) {
	this->categoryPath = path;
}

void Controller::setMaskPixel(int x, int y) {
	polygon->addPoint(x,y);
}

bool Controller::isMaskValid() {
	if (polygon->getPointCount() > 2) return true;
	else return false;
}

void Controller::setMaskFinal() {

	// Berechne die Masken und zeige sie an

	polygon->setFinal();
	computeMask();
	computeBorderMask();
	gui->showMasks(mask, borderMask);
}

void Controller::resetMask() {
	polygon->clear();
}

void Controller::startHoleFilling() {

	// Ertelle Thread, in dem das Model läuft

	Thread^ t1 = gcnew Thread(gcnew ThreadStart(model, &AlgorithmMain::mainLoop));
	model->startAlgorithm(queryImage,mask,categoryPath, bothMasksBB, BBx, BBy);
	t1->Start();
}


void Controller::setResultPictures(array<Bitmap^>^ images) {
	this->gui->showResult(images);
}


void Controller::startProgress(int maxValue, String^ progressText) {
	this->gui->startProgress(maxValue, progressText);
}
	
void Controller::endProgress() {
	this->gui->endProgress();
}
	
void Controller::updateProgress(int value, String^ updateText) {
	this->gui->updateProgress(value, updateText);
}


// PRIVATE METHODEN


bool Controller::areIntersecting(int Ax, int Ay, int Bx, int By, int Cx, int Cy, int Dx, int Dy) {

		double  distAB, theCos, theSin, newX, ABpos ;

		//  Fail if either line segment is zero-length.
		if (Ax==Bx && Ay==By || Cx==Dx && Cy==Dy) return false;

		//  Fail if the segments share an end-point.
		if (Ax==Cx && Ay==Cy || Bx==Cx && By==Cy
			||  Ax==Dx && Ay==Dy || Bx==Dx && By==Dy) {
				return false; }

		//  (1) Translate the system so that point A is on the origin.
		Bx-=Ax; By-=Ay;
		Cx-=Ax; Cy-=Ay;
		Dx-=Ax; Dy-=Ay;

		//  Discover the length of segment A-B.
		distAB=Math::Sqrt(Bx*Bx+By*By);

		//  (2) Rotate the system so that point B is on the positive X axis.
		theCos=Bx/distAB;
		theSin=By/distAB;
		newX=Cx*theCos+Cy*theSin;
		Cy  =Cy*theCos-Cx*theSin; Cx=newX;
		newX=Dx*theCos+Dy*theSin;
		Dy  =Dy*theCos-Dx*theSin; Dx=newX;

		//  Fail if segment C-D doesn't cross line A-B.
		if (Cy<0. && Dy<0. || Cy>=0. && Dy>=0.) return false;

		//  (3) Discover the position of the intersection point along line A-B.
		ABpos=Dx+(Cx-Dx)*Dy/(Dy-Cy);

		//  Fail if segment C-D crosses line A-B outside of segment A-B.
		if (ABpos<0. || ABpos>distAB) return false;

		//  Success.
		return true; 
}




void Controller::computeMask() {
	// Bounding-Box
	int minX = polygon->getMinX();
	int maxX = polygon->getMaxX();
	int minY = polygon->getMinY();
	int maxY = polygon->getMaxY();

	System::Collections::IEnumerator^ myEnum = polygon->sides->GetEnumerator();

	for (int y=0; y < this->height; y++) {
		for (int x=0; x < this->width; x++) {

			// Überprüfen, ob der punkt innerhalb der Bounding-Box liegt

			if ((x>=minX) && (x<=maxX) && (y>=minY) && (y<=maxY)) {

				// Überprüfe, wie oft sich ein strahl mit den seiten des polygons kreuzt
				// Gerade Anzahl: Punkt ist innerhalb von Polygon
				// Ungerade Anzahl: Punkt ist außerhalb von Polygon
				
				// Strahl
				int beamX1 = minX-1;
				int beamY1 = y;
				int beamX2 = x;
				int beamY2 = y;

				int intersections = 0;

				// Gehe über alle Seiten des Polygons

				while (myEnum->MoveNext()) {
					array<int,2>^ side = safe_cast<array<int,2>^>(myEnum->Current);
					if (areIntersecting(beamX1,beamY1,beamX2,beamY2,side[0,0],side[0,1],side[1,0],side[1,1])) {
						intersections++;
					}
				}

				myEnum->Reset();

				if (intersections%2 == 1) {
					mask[x,y] = true;
				} else {
					mask[x,y] = false;
				}

			} else {
				mask[x,y] = false;
			}



		}
	}

}


void Controller::computeBorderMask() {

	for (int y=0; y<this->height; y++) {
		for (int x=0; x<this->width; x++) {
			borderMask[x,y] = false;
		}
	}

	array<bool,2>^ polygonMask = gcnew array<bool,2>(this->width, this->height);

	// Zeichne Umrisse von Polygon in polygonMask
	// (Code aus Internet übernommen, daher keine Kommentare)

	System::Collections::IEnumerator^ myEnum = polygon->sides->GetEnumerator();
	while (myEnum->MoveNext()) {
		array<int,2>^ side = safe_cast<array<int,2>^>(myEnum->Current);
		int xstart = side[0,0];
		int ystart = side[0,1];
		int xend = side[1,0];
		int yend = side[1,1];

		int x, y, t, dx, dy, incx, incy, pdx, pdy, ddx, ddy, es, el, err;
		dx = xend - xstart;
		dy = yend - ystart;
		incx = sgn(dx);
		incy = sgn(dy);
		if(dx<0) dx = -dx;
		if(dy<0) dy = -dy;
		if (dx>dy)
		{
			pdx=incx; pdy=0; 
			ddx=incx; ddy=incy;
			es =dy;   el =dx;
		} else
		{
			pdx=0;    pdy=incy; 
			ddx=incx; ddy=incy;
			es =dx;   el =dy;
		}
		x = xstart;
		y = ystart;
		err = el/2;
		polygonMask[x,y] = true;
		for(t=0; t<el; ++t)
		{
			err -= es; 
			if(err<0)
			{
				err += el;
				x += ddx;
				y += ddy;
			} else
			{
				x += pdx;
				y += pdy;
			}
			polygonMask[x,y] = true;
		}

	}


	// Zeichne Kreis mit 80px radius für jeden Pixel auf dem Polygon
	// (Code aus Internet übernommen, daher keine Kommentare)

	for (int y_=0; y_<this->height; y_++) {
		for (int x_=0; x_<this->width; x_++) {
			if (polygonMask[x_,y_]) {

				int r = 80 >> 1;
				int x;
				int y;

				for(y = 0; y < r << 1; ++y) {
					for(x = 0; x < r << 1; ++x) {
						if((x - r) * (x - r) + (y - r) * (y - r)  <=  r * r) { 
							int posx = x + x_ - 40;
							int posy = y + y_ - 40;
							if ((posx >= 0) && (posx < this->width) && (posy >= 0) && (posy < this->height) && (!mask[posx,posy])){
								borderMask[posx, posy] = true;
							}
							
						}
					}
				}


			}
		}
	}

	// Berechne Bounding Box der BorderMask

	int minX, maxX, minY, maxY = 0;

	bool br = false;
	for (int y=0; y<this->height; y++) {
		if (br) break;
		for (int x=0; x<this->width; x++) {
			if (borderMask[x,y]) {
				minY = y;
				br = true;
				break;
			}
		}
	}
	br = false;
	for (int y=this->height-1; y>=0; y--) {
		if (br) break;
		for (int x=0; x<this->width; x++) {
			if (borderMask[x,y]) {
				maxY = y;
				br = true;
				break;
			}
		}
	}
	br = false;
	for (int x=0; x<this->width; x++) {
		if (br) break;
		for (int y=0; y<this->height; y++) {
			if (borderMask[x,y]) {
				minX = x;
				br = true;
				break;
			}
		}
	}
	br = false;
	for (int x=this->width-1; x>=0; x--) {
		if (br) break;
		for (int y=0; y<this->height; y++) {
			if (borderMask[x,y]) {
				maxX = x;
				br = true;
				break;
			}
		}
	}

	int BBw, BBh = 0;
	if ((maxX-minX+1)>0) BBw = maxX-minX+1;
	if ((maxY-minY+1)>0) BBh = maxY-minY+1;

	// Bounding-Box, die beide Masken enthält
	bothMasksBB = gcnew array<int,2>(BBw,BBh);

	BBx = minX;
	BBy = minY;

	// Fülle Bounding-Box

	for (int x=minX; x<=maxX; x++) {
		for (int y=minY; y<=maxY; y++) {
			if (borderMask[x,y]) {
				bothMasksBB[x-minX,y-minY] = 2;
			} else {
				if (mask[x,y]) {
					bothMasksBB[x-minX,y-minY] = 1;
				} else {
					bothMasksBB[x-minX,y-minY] = 0;
				}
			}
		}
	}

}